
package capa_logica;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.websocket.EncodeException;
import javax.websocket.Session;

// Esta clase hereda de Actor. Define e implmenta las funciones del Drone
public class Drone extends Actor 
{
    private static int LARGO_GRAFO = 12;
    private static int ANCHO_GRAFO = 7;
    private static int TAM_GRAFO = LARGO_GRAFO * ANCHO_GRAFO;
    
    public boolean enMovimiento = false;
    public Session jugador1;
    public Session jugador2;
    public double radio = 50; //Alcance de rescate.
    private double centroX = 0;
    private double centroY = 0;
    private Piloto piloto;
    private Tanque tanque1;
    private Tanque tanque2;
    private boolean hayRescate = false;
    private boolean estoyEnNodo = true;
    private boolean x = true;
    private int nodoConEnemigo1;
    private int nodoConEnemigo2;
    private int escapando;
    private ArrayList<Nodo> nodos;
    private TemporizadorProyectil temp = null; //Si el temporizador es igual a null, el drone nunca se ejecuto.
    
    
    public Drone(int X, int Y, int ene, int vis, int vel, int lar, int anc, int ori, Escenario esc) 
    {
        super(X, Y, ene, vis, vel, lar, anc, ori, esc);
        inicializarNodos();
        nodoConEnemigo1 = -1;
        nodoConEnemigo2 = -1;
        escapando = 0;
    }
    
    public void inicializarNodos()
    {
        nodos = new ArrayList<>();
        for(int i = 0; i < LARGO_GRAFO * ANCHO_GRAFO; i++) 
        {
            if((i > 25 && i < 34) || (i > 37 && i < 46) || (i > 49 && i < 58)) //Nodos centrales 
            { //tienen menos probabilidad de que el piloto se encuentre en ellos
                Nodo n = new Nodo(i, 0, 1);
                nodos.add(n);
            }
            else
            {
                Nodo n = new Nodo(i, 0, 2);
                nodos.add(n);
            } 
        }
    }
    
    public void irANodoOeste()
    {
        orientacion = 270;
        posicionX = posicionX - velocidad;
        if((posicionX % 104) == 52)
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoEste()
    {
        orientacion = 90;
        posicionX = posicionX + velocidad;
        if((posicionX % 104) == 52)
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoNorte()
    { 
        orientacion = 0;
        posicionY = posicionY - velocidad;
        if((posicionY % 100) == 50)
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoSur()
    {
        orientacion = 180;
        posicionY = posicionY + velocidad;
        if((posicionY % 100) == 50)
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoNorEste()
    {
        orientacion = 45;
        posicionX = posicionX + velocidad;
        posicionY = posicionY - velocidad;
        if(((posicionY % 100) == 50) && ((posicionX % 104) == 52))
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoSurEste()
    {
        orientacion = 135;
        posicionX = posicionX + velocidad;
        posicionY = posicionY + velocidad;
        if(((posicionY % 100) == 50) && ((posicionX % 104) == 52))
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoSurOeste()
    {
        orientacion = 225;
        posicionX = posicionX - velocidad;
        posicionY = posicionY + velocidad;
        if(((posicionY % 100) == 50) && ((posicionX % 104) == 52))
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodoNorOeste()
    {
        orientacion = 315;
        posicionX = posicionX - velocidad;
        posicionY = posicionY - velocidad;
        if(((posicionY % 100) == 50) && ((posicionX % 104) == 52))
        {
            estoyEnNodo = true;
        }
    }
    
    public void irANodo(int act, int ady)
    {
        if((act - ady) == -12)
        {
            irANodoSur();
        }
        else if((act - ady == -1))
        {
            irANodoEste();
        }
        else if((act - ady) == 1)
        {
            irANodoOeste();
        }
        else if((act - ady) == 12)
        {
            irANodoNorte();
        }
        else if((act - ady) == -13)
        {
            posicionX = posicionX + velocidad;
            irANodoSurEste();
        }
        else if((act - ady) == -11)
        {
            posicionX = posicionX - velocidad;
            irANodoSurOeste();
        }
        else if((act - ady) == 11)
        {
            posicionX = posicionX + velocidad;
            irANodoNorEste();
        }
        else if((act - ady) == 13)
        {
            posicionX = posicionX - velocidad;
            irANodoNorOeste();
        }
        x = !x; //Cambio este booleano simplemente para indicar que se cargue la imagen del 
               //helicoptero con las helices en X o en cruz
        if(escapando == 0) //No esta escapando
        {
            if(estaDentroDelAlcance(tanque1.getPosicionX(), tanque1.getPosicionY()) ||
                (estaDentroDelAlcance(tanque2.getPosicionX(), tanque2.getPosicionY()))) //Veo un enemigo
            {
                if(nodoConEnemigo1 != -1)
                {
                    nodoConEnemigo2 = ady;
                }
                else
                {
                    nodoConEnemigo1 = ady;
                }
                if(orientacion < 180)
                {
                    irANodo(orientacion + 180); //Vuelvo al ultimo nodo en el que estuve
                }
                else
                {
                    irANodo(orientacion - 180); //Vuelvo al ultimo nodo en el que estuve
                }
                escapando++;
            }    
            
        }
        else //Esta escapando
        {
            if(escapando!=0)
            {
                escapando++;
            }
            if(escapando > 2)
            {
                escapando = 0; //Deja de escapar
            }
        }
    }
    
    public void irANodo(int orientacion)
    {
        switch (orientacion)
        {
            case 180 :
                irANodoSur();
                break;
            case 270 :
                irANodoOeste();
                break;
            case 0 :
                irANodoNorte();
                break;
            case 90 :
                irANodoEste();
                break;
            case 225 :
                irANodoSurOeste();
                break;
            case 135 :
                irANodoSurOeste();
                break;
            case 315 :
                irANodoNorOeste();
                break;
            case 45 :
                irANodoNorEste();
                break;
        }
        x = !x; //Cambio este booleano simplemente para indicar que se cargue la imagen del 
    }           //helicoptero con las helices en X o en cruz
    
    public int nodoActual()
    {
        int n = posicionX / 104;
        int m = posicionY / 100;
        return (n + (12 * m));
    }
    
    public ArregloConTope obtenerAdyacentes(int nodo)
    {
        ArregloConTope a = new ArregloConTope();
        if(((nodo - 13) >= 0) && (nodo % 12 != 0)) //Nodo Noroeste
        {
            a.arreglo[a.tope] = nodo - 13;
            a.tope++;
        }
        if((nodo - 12) >= 0) //Nodo Norte
        {
            a.arreglo[a.tope] = nodo - 12;
            a.tope++;
        }
        if(((nodo - 11) >= 0) && (nodo % 12 != 11)) //Nodo Noreste
        {
            a.arreglo[a.tope] = nodo - 11;
            a.tope++;
        }
        if(((nodo - 1) >= 0) && (nodo % 12 != 0)) //Nodo Oeste
        {
            a.arreglo[a.tope] = nodo - 1;
            a.tope++;
        }
        if(((nodo + 1) < TAM_GRAFO) && (nodo % 12 != 11)) //Nodo Este
        {
            a.arreglo[a.tope] = nodo + 1;
            a.tope++;
        }
        if(((nodo + 11) < TAM_GRAFO) && (nodo % 12 != 0)) //Nodo Suroeste
        {
            a.arreglo[a.tope] = nodo + 11;
            a.tope++;
        }
        if((nodo + 12) < TAM_GRAFO) //Nodo Sur
        {
            a.arreglo[a.tope] = nodo + 12;
            a.tope++;
        }
        if(((nodo + 13) < TAM_GRAFO) && (nodo % 12 != 11)) //Nodo Sureste
        {
            a.arreglo[a.tope] = nodo + 13;
            a.tope++;
        }
        return a;
    }
    
    public int ganancia(int nodoAct, int nodoAdy)
    {
        int dist;
        if(nodoAct - nodoAdy == -1 || nodoAct - nodoAdy == 1)
        {
            dist = 104;
        }
        else if(nodoAct - nodoAdy == -12 || nodoAct - nodoAdy == 12)
        {
            dist = 100;
        }
        else
        {
            dist = 144;
        }
        int g = (nodos.get(nodoAdy).getProbabilidadDePiloto() * 30) - (nodos.get(nodoAdy).getVisitas() * 100) - dist;
        return g;
    }
    
    public boolean movimientoIA()
    {
        if(!estaDentroDelAlcance(piloto.posicionX, piloto.posicionY)) //Si no encontre al piloto
        {
            if(estoyEnNodo) //Si el drone ha alcanzado un nodo del grafo
            {
                int act = nodoActual();
                nodos.get(act).visitar();
                ArregloConTope ady = obtenerAdyacentes(act);
                int mejor1 = ady.arreglo[0];
                int mejor2 = ady.arreglo[1];
                int mejor3 = ady.arreglo[2];
                for (int i = 1; i < ady.tope; i++) //Para todos los nodos adyacentes menos el de posicion 0
                {
                    if((ganancia(act, ady.arreglo[i])) > ganancia(act, mejor1)) //Si es la que me da mas beneficio
                    {
                        mejor3 = mejor2; //La que era la segunda pasa a ser tercera
                        mejor2 = mejor1; //La que era primera pasa a ser segunda
                        mejor1 = ady.arreglo[i]; //La nueva es la mejor
                    }
                    else if((ganancia(act, ady.arreglo[i])) == ganancia(act, mejor1))
                    {
                        double rand = Math.random();
                        if(rand > 0.5) //Para que no tienda a elegir algunos nodos a que otros cuando su ganancia
                                       //es la misma se usa esta funcion aleatoria 
                        {
                            mejor3 = mejor2; //La que era la segunda pasa a ser tercera
                            mejor2 = mejor1; //La que era primera pasa a ser segunda
                            mejor1 = ady.arreglo[i]; //La nueva es la mejor
                        }
                    }
                    else if((ganancia(act, ady.arreglo[i])) > ganancia(act, mejor2)) //Si no es la mejor, pero es mejor que la segunda
                    {
                        mejor3 = mejor2; //La que era la segunda pasa a ser tercera
                        mejor2 = ady.arreglo[i]; //La nueva es la segunda mejor
                    }
                    else if((ganancia(act, ady.arreglo[i])) > ganancia(act, mejor3)) //Si es mejor que la tercera
                    {
                        mejor3 = ady.arreglo[i];
                    }
                }
                estoyEnNodo = false;
                if(mejor1 != nodoConEnemigo1 && mejor1 != nodoConEnemigo2) //Si no tengo informacion de que haya un enemigo en el mejor nodo adyacente
                {
                    irANodo(act, mejor1);
                }
                else if(mejor2 != nodoConEnemigo1 && mejor2 != nodoConEnemigo2) //Sino voy al segundo
                {
                    irANodo(act, mejor2);
                }
                else //Y de ultima opcion al tercero. (Todo nodo tiene un minimo de tres adyacentes)
                {
                    irANodo(act, mejor3);
                }
                ady = null;
            }
            else //No estoy aun en ningun nodo
            {
                irANodo(orientacion); //Sigo en la misma direccion
            }
            return false;
        }
        else
        {
            this.hayRescate = true;
            return true; //Rescate al piloto
        }
    }
    
    public void LanzarDrone()
    {
        Timer temporizador = new Timer("");
        this.temp = new TemporizadorProyectil();
        System.out.println("LANZAR DRONE "+this.getAncho());
        temp.d = this;
        temp.contadorVeces = 0;
        temp.jugador1 = this.jugador1;
        temp.jugador2 = this.jugador2;
        temporizador.schedule(temp, 100, 100);
        
    }
    
    public JsonObject JsonDrone()
    {
        JsonObjectBuilder buildJson = Json.createObjectBuilder().add("jugador", "drone");
        if (!this.hayRescate)
        {
            buildJson = buildJson.add("orden", "mover");
            buildJson = buildJson.add("droneX", this.posicionX);
            buildJson = buildJson.add("droneY", this.posicionY);
            buildJson = buildJson.add("x", x);
            buildJson = buildJson.add("orientacion", orientacion);
            buildJson = buildJson.add("visiblePiloto", elPilotoVeAlDrone());
            buildJson = buildJson.add("visibleTanques", tanquesVenAlDrone());            
        }
        else
        {
            buildJson = buildJson.add("orden", "finalizarJuego");
            buildJson = buildJson.add("jugadorGanador", "jugador1");
        }
        
        JsonObject value = buildJson.build();
        return value; 
    }
    
    public boolean puntoPerteneceACirculo(double x, double y, double r, double h, double k)
    {
        /*  Ecuación del círculo (x - h)^2 + (y - k)^2 = r^2
        (x, y) centro del círculo. r radio. (h, k) punto a testear.*/
        
        double ecuacion = Math.pow(x - h, 2) + Math.pow(y - k, 2);
        
        if (ecuacion <= Math.pow(r, 2))
        {
             return true;
        }
        
        return false;
    }
    
    public boolean tanquesVenAlDrone()
    {
        /* Se debe revisar bien las coordenadas del tanque, el ancho, largo, etc. */
        double centroTanque1X = this.tanque1.getPosicionX() + this.tanque1.getAncho()/ 2;
        double centroTanque1Y = this.tanque1.getPosicionY() + this.tanque1.getLargo()/ 2;
        double centroTanque2X = this.tanque2.getPosicionX() + this.tanque2.getAncho()/ 2;
        double centroTanque2Y = this.tanque2.getPosicionY() + this.tanque2.getLargo()/ 2;        
        
        return puntoPerteneceACirculo(centroTanque1X, centroTanque1Y, this.tanque1.getVision(), 
                                        this.centroX, this.centroY) || 
                puntoPerteneceACirculo(centroTanque2X, centroTanque2Y, this.tanque2.getVision(), 
                                        this.centroX, this.centroY);
    }
    
    public boolean elPilotoVeAlDrone()
    {
        double centroPilotoX = this.piloto.getPosicionX() + this.piloto.getAncho() / 2;
        double centroPilotoY = this.piloto.getPosicionY() + this.piloto.getLargo()/ 2;
        return puntoPerteneceACirculo(centroPilotoX, centroPilotoY, this.piloto.getVision(),
                this.centroX, this.centroY);    
    }
    
    public boolean estaDentroDelAlcance(int X, int Y)
    {
        /*  Esta función devuelve true si el punto (X,Y) se encuentra
            en el lugar geométrico de la circunferencia del alcance
            del Drone.
        */
        //x - h)^2 + (y - k)^2
        this.centroX = this.posicionX + this.ancho / 2;
        this.centroY = this.posicionY + this.largo / 2;
        double ecuacion = Math.pow(this.centroX - X, 2) + Math.pow(this.centroY - Y, 2);
   
        if (ecuacion <= Math.pow(this.radio, 2))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    public class TemporizadorProyectil extends TimerTask 
    {
        //Las veces que se debe repetir el código
        private int veces = 5000;
        private int contadorVeces = 0;
        private Session jugador1;
        private Session jugador2;
        private Drone d;

        public void run() 
        {
            contadorVeces++;

            if (contadorVeces <= veces) 
            {
                try 
                {
                    if(d.movimientoIA())
                    {
                        if (jugador1.isOpen())
                        {
                            jugador1.getBasicRemote().sendObject(this.d.JsonDrone());
                        }
                        if (jugador2.isOpen())
                        {
                            jugador2.getBasicRemote().sendObject(this.d.JsonDrone());
                        }                         
                        this.cancel();
                    }
                    else
                    {
                        if (jugador1.isOpen())
                        {
                            jugador1.getBasicRemote().sendObject(this.d.JsonDrone());
                        }
                        if (jugador2.isOpen())
                        {
                            jugador2.getBasicRemote().sendObject(this.d.JsonDrone());
                        }           
                    }
    
                } 
                catch (IOException ex) 
                {
                    Logger.getLogger(Drone.class.getName()).log(Level.SEVERE, null, ex);
                } 
                catch (EncodeException ex) 
                {
                    Logger.getLogger(Drone.class.getName()).log(Level.SEVERE, null, ex);
                }
            } 
            else 
            {
                this.cancel();
            }
        }
    }
        
    // Getters and Setters
        
    public Tanque getTanque1() 
    {
        return tanque1;
    }

    public void setTanque1(Tanque tanque1) 
    {
        this.tanque1 = tanque1;
    }

    public Tanque getTanque2() 
    {
        return tanque2;
    }

    public void setTanque2(Tanque tanque2) 
    {
        this.tanque2 = tanque2;
    }
   
    public Piloto getPiloto() 
    {
        return piloto;
    }

    public void setPiloto(Piloto piloto) 
    {
        this.piloto = piloto;
    }
    
    public void matoDrone(){
        System.out.println("MATO EL DRONE"+(this.temp != null));
        if (this.temp != null)
            this.temp.cancel();
    }
}
